/*
 * Copyright (c) 2010 Mathew Hall, University of Sheffield.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following
 * disclaimer in the documentation and/or other materials provided
 * with the distribution.
 *
 * Neither the name of the University of Sheffield nor the names of its
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package search.fitnessfunctions;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import primitives.cluster.ClusterHead;
import primitives.cluster.IClusterLevel;
import primitives.graph.Node;
import primitives.graph.Transition;

public class SimpleXorFitnessFunction extends TreeFitnessFunction {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5832723875534608893L;

	public double evaluate(ClusterHead tree) {
		double total = 0;
		HashMap<Node, IClusterLevel> cs = new HashMap<Node, IClusterLevel>();

		for (IClusterLevel t : tree.getChildren()) {
			for (Node n : t.getNodes()) {
				cs.put(n, t);
			}
		}

		for (IClusterLevel t : tree.getChildren())
			total += countXorClusters(t, cs);

		return total;
	}

	public static double countXorClusters(IClusterLevel cluster,
			HashMap<Node, IClusterLevel> cs) {

		// initialise this so we can lookup what cluster each node lives in

		Set<IClusterLevel> required = new HashSet<IClusterLevel>();

		// each node in this cluster
		// get transitions
		// if this cluster contains destination node fine
		// otherwise all states need to go to this node/cluster - node for now.
		for (Node n : cluster.getNodes()) {

			Set<Transition> s = n.getTransitionsAsT();

			for (Transition t : s) {
				if (cluster.contains(t.getDestinationNode())) {
					// no problem
				} else {
					// put the cluster the target node into required
					required.add(cs.get(t.getDestinationNode()));
					// once this list of required is built up go
					// down the nodes again - for every target node
					// or cluster (cluster in this case) make sure it
					// goes to every one in required, if it doesn't
					// then there's a problem.

					// can optimise this by doing this check as required is
					// built
					// so we can finish earlier too.

				}
			}
		}

		double res = 0;

		// go over each again
		for (Node n : cluster.getNodes()) {
			Set<Transition> s = n.getTransitionsAsT();
			Set<IClusterLevel> thisreq = new HashSet<IClusterLevel>();
			for (Transition t : s) {
				if (cluster.contains(t.getDestinationNode())) {
					// no problem
				} else {
					// put the cluster the target node into required
					thisreq.add(cs.get(t.getDestinationNode()));
					// once this list of required is built up go
					// down the nodes again - for every target node
					// or cluster (cluster in this case) make sure it
					// goes to every one in required, if it doesn't
					// then there's a problem.

					// can optimise this by doing this check as required is
					// built
					// so we can finish earlier too.

				}
			}
			if (required.size() != 0)
				res += thisreq.size() / required.size();
		}
		if (cluster.getNodes().size() == 1)
			return 0;
		// if(required.size() == 0) return 1;
		return res;
	}

}
